/*
 * FeedRepresentation.java
 *
 * Created on March 27, 2007, 11:55 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package org.atomojo.app;

import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLDecoder;
import java.util.Date;
import java.util.Iterator;
import java.util.logging.Level;
import org.atomojo.app.db.Entry;
import org.atomojo.app.db.Feed;
import org.atomojo.app.db.Term;
import org.atomojo.app.db.TermInstance;
import org.infoset.xml.DocumentLoader;
import org.infoset.xml.Element;
import org.infoset.xml.InfosetFactory;
import org.infoset.xml.ItemConstructor;
import org.infoset.xml.ItemDestination;
import org.infoset.xml.Name;
import org.infoset.xml.XMLException;
import org.infoset.xml.filter.RemoveDocumentFilter;
import org.infoset.xml.sax.SAXDocumentLoader;
import org.infoset.xml.util.WriterItemDestination;
import org.restlet.data.CharacterSet;
import org.restlet.data.Form;
import org.restlet.data.MediaType;
import org.restlet.data.Reference;
import org.restlet.representation.OutputRepresentation;
import org.restlet.representation.Representation;
import org.restlet.resource.ServerResource;

/**
 *
 * @author alex
 */
public class TermResource extends ServerResource {

   static final URI CHILD_TERM = URI.create("http://www.atomojo.org/O/relations/child");
   static final URI ANCESTOR_TERM = URI.create("http://www.atomojo.org/O/relations/ancestor");
   Reference resourceBase;
   App app;
   URI term;
   
   /** Creates a new instance of FeedRepresentation */
   public TermResource(Reference resourceBase,App app,URI term) {
      setNegotiated(false);
      this.resourceBase = resourceBase;
      this.app = app;
      this.term = term;
   }
   
   public Representation get() {
      return new OutputRepresentation(MediaType.APPLICATION_ATOM_XML) {
         public void write(OutputStream os)
            throws IOException
         {
            setCharacterSet(CharacterSet.UTF_8);
            try {
               Form form = getRequest().getResourceRef().getQueryAsForm();
               String value = form.getFirstValue("value");
               generate(new WriterItemDestination(new OutputStreamWriter(os,"UTF-8"),"UTF-8"),value);
            } catch (XMLException ex) {
               getContext().getLogger().log(Level.SEVERE,"Cannot serialize metadata feed.",ex);
               throw new IOException("Cannot serialize metadata feed: "+ex.getMessage());
            }
         }
      };
   }
   
   public void generate(ItemDestination dest,String value)
      throws XMLException
   {
      //getContext().getLogger().info("Generating feed for term "+term);
      ItemConstructor constructor = InfosetFactory.getDefaultInfoset().createItemConstructor();
      dest.send(constructor.createDocument());
      dest.send(constructor.createElement(AtomResource.FEED_NAME));
      dest.send(constructor.createCharacters("\n"));
      link(constructor,dest,"self",getRequest().getResourceRef().toString());
      dest.send(constructor.createCharacters("\n"));
      text(constructor,dest,AtomResource.TITLE_NAME,"Term "+term);
      dest.send(constructor.createCharacters("\n"));
      text(constructor,dest,AtomResource.ID_NAME,getRequest().getResourceRef().toString());
      dest.send(constructor.createCharacters("\n"));
      text(constructor,dest,AtomResource.UPDATED_NAME,AtomResource.toXSDDate(new Date()));
      term(constructor,dest,term,null);
      try {
         Term dbTerm = app.getDB().findTerm(term);
         if (dbTerm!=null) {
            Iterator<TermInstance<Feed>> feeds = app.getDB().getFeedsByTerm(dbTerm,value);
            while (feeds.hasNext()) {
               TermInstance<Feed> termValue = feeds.next();
               Feed feed = termValue.getTarget();
               dest.send(constructor.createCharacters("\n"));
               dest.send(constructor.createElement(AtomResource.ENTRY_NAME));
               text(constructor,dest,AtomResource.ID_NAME,feed.getUUID().toString());
               text(constructor,dest,AtomResource.PUBLISHED_NAME,AtomResource.toXSDDate(feed.getCreated()));
               text(constructor,dest,AtomResource.UPDATED_NAME,AtomResource.toXSDDate(feed.getEdited()));
               text(constructor,dest,AtomResource.TITLE_NAME,app.getStorage().getFeedTitle(feed.getPath(),feed.getUUID()));
               term(constructor,dest,Categorization.FEED_TYPE_TERM,null);
               Iterator<TermInstance<Feed>> terms = feed.getTerms();
               while (terms.hasNext()) {
                  TermInstance<Feed> fterm = terms.next();
                  term(constructor,dest,fterm.getTerm().getURI(),fterm.getValue());
               }
               link(constructor,dest,"related",resourceBase+feed.getPath());
               dest.send(constructor.createElementEnd(AtomResource.ENTRY_NAME));
            }
            DocumentLoader loader = new SAXDocumentLoader();
            Iterator<TermInstance<Entry>> entries = app.getDB().getEntriesByTerm(dbTerm,value);
            while (entries.hasNext()) {
               TermInstance<Entry> termValue = entries.next();
               Entry entry = termValue.getTarget();
               Feed feed = entry.getFeed();
               String feedPath = feed.getPath();
               String feedBaseURI = resourceBase.toString()+feedPath;
               dest.send(constructor.createCharacters("\n"));

               // get the entry representation
               Representation rep = app.getStorage().getEntry(feedBaseURI,feedPath,feed.getUUID(),entry.getUUID());
               
               // avoid thread creation because reading an output representation requires a thread
               StringWriter sw = new StringWriter();
               rep.write(sw);
               rep.release();
               loader.generate(new StringReader(sw.toString()), new RemoveDocumentFilter(dest));
               
            }
         }
      } catch (Exception ex) {
         throw new XMLException("Exception while getting ancestors.",ex);
      }
      dest.send(constructor.createCharacters("\n"));
      dest.send(constructor.createElementEnd(AtomResource.FEED_NAME));
      dest.send(constructor.createDocumentEnd());
   }
   
   static void text(ItemConstructor constructor,ItemDestination dest,Name name,String text)
      throws XMLException
   {
      dest.send(constructor.createElement(name));
      dest.send(constructor.createCharacters(text));
      dest.send(constructor.createElementEnd(name));
   }
      
   static void term(ItemConstructor constructor,ItemDestination dest,URI term,Object value)
      throws XMLException
   {
      String [] parts = new String[2];
      Term.split(term,parts);
      try {
         Element termE = constructor.createElement(AtomResource.CATEGORY_NAME);
         if (!parts[0].equals("http://www.atomojo.org/O/keyword/")) {
            termE.setAttributeValue("scheme",parts[0]);
         }
         termE.setAttributeValue("term",URLDecoder.decode(parts[1],"UTF-8"));
         dest.send(termE);
         if (value!=null && !(value instanceof Nil)) {
            dest.send(constructor.createCharacters(value.toString()));
         }

         dest.send(constructor.createElementEnd(AtomResource.CATEGORY_NAME));
      } catch (UnsupportedEncodingException ex) {
         throw new XMLException("Cannot decode term "+parts[1],ex);
      }
   }
   static void link(ItemConstructor constructor,ItemDestination dest,String rel,String href)
      throws XMLException
   {
      Element linkE = constructor.createElement(AtomResource.LINK_NAME);
      linkE.setAttributeValue("rel",rel);
      linkE.setAttributeValue("href",href);
      linkE.setAttributeValue("type","application/atom+xml");
      dest.send(linkE);
      
      dest.send(constructor.createElementEnd(AtomResource.LINK_NAME));
   }
}
